import '../styles/style.css';

/**
 * 媒体查询项对应的参数配置
 * @param {string} item 表示媒体查询值
 * @param {number} divisor 除数 (即每行 row 占几个列 col)
 * @param {number} span 表示单个 col 所占的栅格数
 * @param {string} label 对应的值
 */
const __mediaQueries = [
    { item: '(min-width: 1400px)', divisor: 4, span: 3, label: 'xl' },
    {
        item: '(min-width: 992px) and (max-width: 1400px)',
        divisor: 3,
        span: 4,
        label: 'lg',
    },
    {
        item: '(min-width: 768px) and (max-width: 992px)',
        divisor: 2,
        span: 6,
        label: 'md',
    },
];

/**
 * 媒体查询值发生改变
 * @param {object} mediaQ 当前匹配到的媒体查询项的参数配置
 */
const __onMediaQueryChange = function (mediaQ) {
    // 显示全部 col
    this.__colWrapper.querySelectorAll('.d-none').forEach((x) => {
        x.classList.remove('d-none');
    });

    // 被除数
    let dividend = 0;
    // 非展开状态，并且 总数 > 除数
    if (!this.__isExpand && this.__colsTotalCount > mediaQ.divisor) {
        for (let i = mediaQ.divisor - 1; i < this.__colsTotalCount - 1; i++) {
            this.__colWrapper.children[i].classList.add('d-none');
        }
        dividend = mediaQ.divisor;
    } else {
        dividend = this.__colsTotalCount;
    }

    // 计算最后操作按钮所在 col 的偏移值
    const offset = 12 - (dividend % mediaQ.divisor) * mediaQ.span;
    this.__colLast.className = 'col';
    if (offset > 0 && offset < 12) {
        this.__colLast.classList.add(`offset-${mediaQ.label}-${offset}`);
    }
};

/**
 * 媒体查询处理
 */
const __mediaQueryHandler = function (list) {
    if (list.matches) {
        const item = __mediaQueries.find((x) => x.item === list.media);
        if (item) {
            __onMediaQueryChange.call(this, item);
        }
    }
};

/**
 * 监听定义的媒体查询项
 * @param {bool} isListener 是否添加监听
 */
const __mediaQueryListener = function (isListener) {
    for (let i = 0; i < __mediaQueries.length; i++) {
        const list = window.matchMedia(__mediaQueries[i].item);
        __mediaQueryHandler.call(this, list);

        // 只有组件初始化时才添加监听
        if (isListener === true) {
            list.addEventListener('change', (x) => __mediaQueryHandler.call(this, x));
        }
    }
};

/**
 * 支持的元素类型
 */
const ITEM_TYPE = {
    TEXT: 'text',
    SELECT: 'select',
    DROPDOWN: 'dropdown',
    DATE_PICKER: 'date-picker',
    DATE_RANGE_PICKER: 'date-range-picker',
    AUTO_COMPLETE: 'auto-complete',
};

/**
 * 自定义 FormReset 事件对象
 */
const __formResetEvent = new Event('form-reset');

/**
 * 获取 Dropdown 选中项的 path 值
 * @param {HTMLLinkElement} srcElement 事件源对象
 * @param {string[]} arr 保存结果的数组
 */
const __getDropdownSelectedPath = function (srcElement, arr = []) {
    arr.push(srcElement.innerHTML);
    // 向上查找
    const el = srcElement.closest('.dropdown-menu').previousElementSibling;
    if (el.tagName.toUpperCase() === 'A') {
        return __getDropdownSelectedPath(el, arr);
    }
    return arr;
};

/**
 * 创建 dropdown 元素的 item 项
 * @param {*} item
 * @returns
 */
const __createDropdownItem = function (input, item) {
    const menuItem = document.createElement('li');
    if (!Array.isArray(item.children)) {
        const link = document.createElement('a');
        link.href = '#';
        link.className = 'dropdown-item position-relative';
        link.innerHTML = item.label;
        link.setAttribute('data-item-value', item.value);
        link.onclick = function (e) {
            const v = this.getAttribute('data-item-value');
            input.setAttribute('data-value', v);
            // 向上查找path
            const r = __getDropdownSelectedPath(this).reverse();
            input.value = r.join(' / ');
        };
        menuItem.appendChild(link);
        return menuItem;
    }

    menuItem.className = 'dropdown dropend';

    const link = document.createElement('a');
    link.href = '#';
    link.className = 'dropdown-item dropdown-toggle';
    link.setAttribute('data-bs-toggle', 'dropdown');
    //link.setAttribute("aria-haspopup", "true");
    link.setAttribute('data-item-value', item.value);
    link.setAttribute('aria-expanded', 'true');
    // 监听点击事件
    link.addEventListener('click', function (e) {
        const els = this.parentNode.parentNode.children;
        for (let i = 0; i < els.length; i++) {
            if (els[i] === this.parentNode) {
                continue;
            }

            // 点击当前菜单按钮时，隐藏掉其它子菜单
            bootstrap.Dropdown.getInstance(els[i].querySelector('[aria-expanded="true"]'))?.hide();
        }

        // 阻止冒泡触发菜单关闭
        e.stopPropagation();
    });
    link.innerHTML = item.label;
    menuItem.appendChild(link);

    const childWrapper = document.createElement('ul');
    childWrapper.className = 'dropdown-menu';
    childWrapper.setAttribute('data-popper-placement', 'right-start');
    item.children.forEach((x) => {
        childWrapper.appendChild(__createDropdownItem(input, x));
    });
    menuItem.appendChild(childWrapper);
    return menuItem;
};

/**
 * 从指定树状结构查找对象 path
 * @param {object[]} tree 树状结构数据
 * @param {string} v 要查找的 value 值
 * @param {*} currentPath 结果
 * @example
 * var r = __findPathFromTree([
 *      { value: 'a', children: [
 *          { value: 'a-1', children: [
 *              { value: 'a-1-1' }
 *          ] }
 *      ] }
 * ], 'a-1-1')
 * @returns
 */
const __findPathFromTree = function (tree, v, currentPath = []) {
    // 遍历数组中的每个节点
    for (const node of tree) {
        // 将当前节点的值添加到路径中
        const updatedPath = [...currentPath, { value: node.value, label: node.label }];
        // 如果当前节点的值匹配目标值，则返回该节点和路径
        if (node.value === v) {
            return { node, path: updatedPath };
        }

        // 如果当前节点有子节点，则递归调用 findNodeAndRecordPath 在子节点中查找
        if (node.children && node.children.length > 0) {
            const resultInChildren = __findPathFromTree(node.children, v, updatedPath);
            if (resultInChildren.node) {
                return resultInChildren; // 如果在子节点中找到，直接返回
            }
        }
    }

    // 如果在当前层次未找到匹配节点，则返回空对象
    return { node: null, path: [] };
};

/**
 * 元素类型处理器数组
 */
const __elementProcessors = [
    {
        // <input type="text" />
        type: ITEM_TYPE.TEXT,
        create: function (item) {
            const input = document.createElement('input');
            input.name = item.inputName;
            input.className = 'form-control';
            input.placeholder = item.label;
            input.setAttribute('data-search-type', ITEM_TYPE.TEXT);

            if (item.defaultValue) {
                input.value = item.defaultValue;
            }

            return input;
        },
        setValue: function (input, value) {
            input.value = value;
        },
    },
    {
        // <select>
        type: ITEM_TYPE.SELECT,
        create: function (item) {
            const select = document.createElement('select');
            select.name = item.inputName;
            select.className = 'form-select';
            select.title = item.label;
            select.add(new Option('请选择' + item.label, ''));
            select.setAttribute('data-search-type', ITEM_TYPE.SELECT);

            // 填充数据
            item.data.forEach((x) => {
                select.add(new Option(x.label, x.value));
            });

            if (item.defaultValue) {
                select.value = item.defaultValue;
            }

            return select;
        },
        setValue: function (input, value) {
            input.value = value;
        },
    },
    {
        // dropdown
        type: ITEM_TYPE.DROPDOWN,
        create: function (item) {
            const ddl = document.createElement('div');
            ddl.className = 'dropdown form-dropdown';
            // 选框
            const input = document.createElement('input');
            input.className = 'form-select';
            input.name = item.inputName;
            input.placeholder = item.label;
            input.autocomplete = 'off';
            input.setAttribute('data-bs-toggle', 'dropdown');
            // input.setAttribute('data-bs-auto-close', 'false');
            input.setAttribute('data-search-type', ITEM_TYPE.DROPDOWN);
            //input.setAttribute("aria-expanded", true);

            if (item.defaultValue) {
                const r = __findPathFromTree(item.data, item.defaultValue);
                if (r.path.length > 0) {
                    input.value = r.path.map((x) => x.label).join(' / ');
                    input.setAttribute('data-value', r.node.value);
                }
            }

            ddl.appendChild(input);
            // 子项
            const ul = document.createElement('ul');
            ul.className = 'dropdown-menu';

            item.data.forEach((x) => {
                ul.appendChild(__createDropdownItem(input, x));
            });

            ddl.appendChild(ul);
            return ddl;
        },
        setValue: function (input, value) {
            const item = this.__opts.items.find((x) => x.inputName === input.name);
            const r = __findPathFromTree(item.data, value);
            if (r.path.length > 0) {
                input.value = r.path.map((x) => x.label).join(' / ');
                input.setAttribute('data-value', r.node.value);
            }
        },
    },
    {
        // date-picker
        type: ITEM_TYPE.DATE_PICKER,
        create: function (item) {
            const input = document.createElement('input');
            input.className = 'form-select';
            input.name = item.inputName;
            input.placeholder = item.label;
            input.setAttribute('data-search-type', ITEM_TYPE.DATE_PICKER);

            if (item.defaultValue) {
                input.value = item.defaultValue;
            }

            const fp = flatpickr(input, { locale: 'zh' });
            // 监听自定义 form-reset 事件，表单重置时清除 DatePicker 选中项
            input.addEventListener(__formResetEvent.type, function (e) {
                fp.clear();
            });
            return input;
        },
        setValue: function (input, value) {
            input.value = value;
        },
    },
    {
        // date-range-picker
        type: ITEM_TYPE.DATE_RANGE_PICKER,
        create: function (item) {
            const input = document.createElement('input');
            input.className = 'form-select';
            input.name = item.inputName;
            input.placeholder = item.label;
            input.setAttribute('data-search-type', ITEM_TYPE.DATE_RANGE_PICKER);

            if (item.defaultValue) {
                input.value = item.defaultValue.join(' 至 ');
            }

            const fp = flatpickr(input, { locale: 'zh', mode: 'range' });
            // 监听自定义 form-reset 事件，表单重置时清除 DatePicker 选中项
            input.addEventListener(__formResetEvent.type, function (e) {
                fp.clear();
            });
            return input;
        },
        setValue: function (input, value) {
            input.value = value.join(' 至 ');
        },
    },
    {
        type: ITEM_TYPE.AUTO_COMPLETE,
        create: function (item, opts) {
            const input = document.createElement('input');
            input.className = 'form-select typeahead';
            input.placeholder = item.label;
            input.name = item.inputName;
            input.title = item.label;
            input.setAttribute('data-search-type', ITEM_TYPE.AUTO_COMPLETE);

            if (item.defaultValue) {
                const v = opts.source
                    .find((x) => x.key === item.inputName)
                    ?.data.find((x) => x.value === item.defaultValue);
                if (v) {
                    input.value = v.label;
                    input.setAttribute('data-value', item.defaultValue);
                }
            }

            return input;
        },
        /**
         * 搜索组件创建完毕，已渲染到页面中后执行的方法
         */
        onRendered: function () {
            // 数据源
            const source = this.__opts.source;
            const dict = this.__opts.dict;
            // 查找自动完成 input 元素
            const input = this.__colWrapper.querySelector('input');
            // 找到 input 元素对应数据源，根据 input.name 属性查找
            const data = source?.find((x) => x.key === input.name)?.data;
            if (!data) {
                return;
            }

            // Bloodhound 是 Typeahead.js 中用于数据源管理的引擎
            const engine = new Bloodhound({
                // 使用对象的 label 属性进行分词
                // datumTokenizer: Bloodhound.tokenizerswhitespace,
                // datumTokenizer: Bloodhound.tokenizers.obj.whitespace("label"),
                // 返回句子的分词结果数组
                datumTokenizer: function (d) {
                    // 浏览器原生分词
                    const words = Array.from(new Intl.Segmenter('zh', { granularity: 'word' }).segment(d.label))
                        .filter((x) => x.isWordLike === true)
                        .map((x) => x.segment);

                    // 自定义词典
                    if (Array.isArray(dict)) {
                        dict.forEach((w) => {
                            if (d.label.indexOf(w) > -1) {
                                words.push(w);
                            }
                        });
                    }
                    return words;
                },
                // input 输入的查询分词
                queryTokenizer: Bloodhound.tokenizers.whitespace,
                // 数据源
                local: data,
                // 如果设置为 false，则不会由构造函数隐式初始化 Bloodhound 实例。默认为 true ， 还可以调用 engine.initialize() 主动初始化
                initialize: true,
                // 可以通过 engine.get([1, 2]) 获取默认建议
                identify: function (obj) {
                    return obj.value;
                },
                /*
                // 向远程请求数据， %QUERY 表示文本框输入的值
                remote: {
                    url: "/search?keyword=%QUERY",
                    wildcard: "%QUERY",
                    // 数据格式转换
                    transport: function(url, options, onSuccess, onError) {
                    }
                },
                */
            });

            /*
            function withDefaults(q, sync) {
                // q 表示 input 输入的关键词
                if (q === "") {
                    sync(engine.get([1, 2])); // 默认建议
                } else {
                    //engine.search(q, sync);
                    console.log(engine)
                }
            }
            */

            const el = $(input);
            el.typeahead(
                {
                    // 高亮匹配的关键字
                    highlight: true,
                    // 最小输入字符数
                    minLength: 2,
                    classNames: {
                        menu: 'dropdown-menu',
                        suggestion: 'dropdown-item',
                    },
                },
                {
                    // 命名空间
                    name: input.name,
                    displayKey: 'label',
                    // 数据源
                    //source: withDefaults,
                    source: engine.ttAdapter(),
                    // 列表结果最大数量限制
                    limit: 20,
                }
            );

            // 选中建议时，将选中值记录到元素的 data-value 属性上
            el.bind('typeahead:select', function (e, suggestion) {
                this.setAttribute('data-value', suggestion.value);
            });

            // 这里就是 input 原生 change 事件
            el.bind('typeahead:change', function (e, v) {
                // v 就是文本框输入值
                if (!v) {
                    this.removeAttribute('data-value');
                }
            });

            // 表单重置时，清空选中项
            input.addEventListener(__formResetEvent.type, function () {
                el.typeahead('val', '');
            });

            const typeaheadWrapper = this.__colWrapper.querySelector('.twitter-typeahead');
            typeaheadWrapper.style.display = 'block';
            // typeahead.js 会自动在 tt-hint 元素上添加一个白色背景，这里设置为透明背景
            typeaheadWrapper.querySelector('.tt-hint').style.backgroundColor = 'transparent';
        },

        setValue: function (input, value) {
            const v = this.__opts.source.find((x) => x.key === input.name)?.data.find((x) => x.value === value);
            if (v) {
                input.value = v.label;
                input.setAttribute('data-value', value);
            }
        },
    },
];

/**
 * 获取表单的各项查询参数
 */
const __getParameters = function () {
    const r = {};
    const cols = this.__colWrapper.children;
    // 最后是操作按钮 col ， 所以 -1
    for (let i = 0; i < cols.length - 1; i++) {
        if (cols[i].classList.contains('d-none')) {
            break;
        }

        // 获取数据元素
        const el = cols[i].querySelector('[name]');
        let v;
        switch (el.getAttribute('data-search-type')) {
            case ITEM_TYPE.DROPDOWN:
                v = el.getAttribute('data-value');
                break;
            case ITEM_TYPE.DATE_RANGE_PICKER:
                v = (el.value && el.value.split(' 至 ')) || [];
                break;
            case ITEM_TYPE.AUTO_COMPLETE:
                v = el.getAttribute('data-value');
                break;
            default:
                v = el.value;
                break;
        }
        r[el.name] = v;
    }
    return r;
};

const __setParameters = function (parameters) {
    for (const key in parameters) {
        const input = this.__colWrapper.querySelector('[name="' + key + '"]');
        if (!input) {
            continue;
        }

        const processor = __elementProcessors.find((x) => x.type === input.getAttribute('data-search-type'));
        processor?.setValue?.call(this, input, parameters[key]);
    }
};

/**
 * 创建操作按钮 col
 */
const __createActionColumn = function () {
    const div = document.createElement('div');
    div.className = 'col';

    // 包装
    const buttonWrapper = document.createElement('div');
    buttonWrapper.className = 'd-flex align-items-center justify-content-end';

    // 搜索按钮
    const searchButton = document.createElement('button');
    searchButton.type = 'button';
    searchButton.className = `btn btn-primary btn-sm me-2 ${this.__opts.btnClassName}`;
    searchButton.innerHTML = '<i class="bi bi-search" style="font-size:.75rem"></i> 查询';
    searchButton.addEventListener('click', () => {
        const parameters = __getParameters.call(this);
        this.__opts.onSearch(parameters);
    });
    buttonWrapper.appendChild(searchButton);

    // 重置按钮
    const resetButton = document.createElement('button');
    resetButton.type = 'reset';
    resetButton.className = `btn btn-secondary btn-sm me-2 ${this.__opts.btnClassName}`;
    resetButton.innerHTML = '<i class="bi bi-x-circle" style="font-size:.75rem"></i> 重置';
    // resetButton.addEventListener('click', () => {});
    buttonWrapper.appendChild(resetButton);

    // 展开按钮
    const expandButton = document.createElement('a');
    expandButton.href = '#';
    expandButton.innerHTML = this.__opts.expandInnerHtml;
    expandButton.addEventListener('click', (e) => {
        e.preventDefault();
        this.expandToggle(e.currentTarget);
    });
    buttonWrapper.appendChild(expandButton);

    div.appendChild(buttonWrapper);
    return div;
};

/**
 * 根据提供的配置项 items，创建对应类型的数据元素
 */
const __createElement = function (item) {
    //console.log(item)
    // 查找对应元素类型的处理器
    const processor = __elementProcessors.find((x) => x.type === item.type);

    if (!processor) {
        console.error('不支持该类型的元素');
        return;
    }

    return processor.create(item, this.__opts);
};

/**
 * 创建 col
 * @param {object} item 表示外部定义的 items 的单个数据项
 */
const __createColumn = function (item) {
    // col
    const col = document.createElement('div');
    col.className = 'col';
    // row
    const innerRow = document.createElement('div');
    innerRow.className = 'row';
    // label
    const label = document.createElement('label');
    label.className = 'col-sm-4 col-form-label';
    label.innerHTML = item.label;
    innerRow.appendChild(label);
    // 子组件的 wrapper
    const elementWrapper = document.createElement('div');
    elementWrapper.className = 'col-sm-8';
    // 数据组件
    const element = __createElement.call(this, item);
    if (element) {
        elementWrapper.appendChild(element);
    }
    innerRow.appendChild(elementWrapper);
    col.appendChild(innerRow);
    return col;
};

/**
 * 渲染组件 dom
 */
const __render = function () {
    const row = document.createElement('div');
    row.className = 'row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xxl-4 gy-3 m-0';
    row.setAttribute('col-wrapper', '');
    for (let i = 0; i < this.__opts.items.length; i++) {
        const col = __createColumn.call(this, this.__opts.items[i]);
        row.appendChild(col);
    }
    row.appendChild(__createActionColumn.call(this));
    const form = document.createElement('form');

    // 重置表单
    const __this = this;
    form.onreset = function () {
        // 移除设置在元素上的数据
        this.querySelectorAll('[data-value]').forEach((x) => {
            x.value = '';
            x.removeAttribute('data-value');
        });

        this.querySelectorAll(
            `[data-search-type="${ITEM_TYPE.DATE_PICKER}"],[data-search-type="${ITEM_TYPE.DATE_RANGE_PICKER}"],[data-search-type="${ITEM_TYPE.AUTO_COMPLETE}"]`
        ).forEach((x) => {
            x.dispatchEvent(__formResetEvent);
        });

        // 表单重置事件
        __this.__opts?.onReset?.();
    };
    form.appendChild(row);
    this.__container.appendChild(form);
};

/**
 * 搜索组件
 */
class Search {
    constructor(selector, options) {
        if (!Array.isArray(options.items)) {
            return;
        }

        const __defaultOptions = {
            // 展开按钮 InnerHTML
            expandInnerHtml:
                '展开<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down ms-1" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708"/></svg>',
            // 展开按钮 InnerHTML
            reduceInnerHtml:
                '收起<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-up ms-1" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708z"/></svg>',

            // 额外的按钮样式名
            btnClassName: '',
            /**
             * 查询按钮事件
             */
            onSearch: function (parameters) {
                console.log(parameters);
            },

            // 表单重置
            onReset: function () {},
        };

        this.__opts = $.extend(true, __defaultOptions, options);
        // 组件最外层元素
        this.__container = document.querySelector(selector);
        this.__container.classList.add('nimble-search');
        // col 总数， 1 表示操作按钮本身的 col
        this.__colsTotalCount = options.items.length + 1;
        // 展开状态
        this.__isExpand = false;
        // 渲染组件 dom
        __render.call(this);
        // col 外层容器
        this.__colWrapper = this.__container.querySelector('[col-wrapper]');
        // 操作按钮所在 col 元素
        this.__colLast = this.__colWrapper.lastElementChild;

        // 监听定义的媒体查询项
        __mediaQueryListener.call(this, true);

        // 组件创建完毕后，执行指定类型元素的自定义处理事件
        __elementProcessors.forEach((x) => {
            if (typeof x.onRendered === 'function') {
                x.onRendered.call(this);
            }
        });

        // 执行搜索函数
        this.search = (parameters) => {
            // 设置搜索参数
            __setParameters.call(this, parameters);
            // 点击搜索按钮
            this.__colLast.querySelector('button[type=button]').click();
        };
    }

    /**
     * 展开/收起切换事件
     */
    expandToggle(srcElement) {
        if (this.__isExpand) {
            this.__isExpand = false;
            srcElement.innerHTML = this.__opts.expandInnerHtml;
        } else {
            this.__isExpand = true;
            srcElement.innerHTML = this.__opts.reduceInnerHtml;
        }
        __mediaQueryListener.call(this);
    }
}

export { Search, ITEM_TYPE };
